package edu.siu.math.egut.main;

import java.io.IOException;
import java.util.regex.Matcher;

import edu.siu.math.egut.io.Algorithms;
import edu.siu.math.egut.io.LiE;
import edu.siu.math.egut.io.OutputPref;
import edu.siu.math.egut.io.ParabolicException;
import edu.siu.math.egut.io.ParabolicPieceException;
import edu.siu.math.egut.io.SunipotentFormatException;
import edu.siu.math.egut.util.Group;
import edu.siu.math.egut.util.Matrix;
import edu.siu.math.egut.util.MatrixMultiplicationException;
import edu.siu.math.egut.util.NoSuchElementException;
import edu.siu.math.egut.util.NotARootException;
import edu.siu.math.egut.util.Polynomial;
import edu.siu.math.egut.util.PolynomialReadException;
import edu.siu.math.egut.util.Sunipotent;

public class PieceRep {

    /**
     * Let P =MU be a parabolic subgroup of a reductive group G.  Let {i1 < i2 < ... < ik}.
     * Let r be the semisimple rank of G and identify roots of G with r-tuples of integers 
     * by writing them in terms of the basis of simple roots.  Define a mapping Z^r -> Z^k by 
     * (n[1], ... n[r]) ->( n[i1], ..., n[ik]).  
     * Each of the fibers of this map defines a M-module.  
     * A realization of G determines a basis for each of these M-modules.  The purpose of this 
     * program is to compute the matrices of elements of M with respect to these bases.
     * 
     * 
     * Syntax:  PieceRep Group Parabolic PieceSpecifier Elements 
     * 
     * @param args need to specify the group G, parabolic P, and which piece we consider, and finally an element of M.
     */
    public static void main(String[] args) {
	// extract input from args
	// args[0] is supposed to be the name of a group.  Check that it is one. 
	Group g = Group.getGroup(args[0]);
	if(g == null){
	    System.out.println("First argument --"+ args[0] + "-- is not the name of an exceptional group.");
	    System.exit(1);
	}
	try {
		/* args[1] should specify a parabolic subgroup of that exceptional group.  therefore, 
		 *   (1) every character in it should be a digit between 1 and the rank of g inclusive, and 
		 *   (2) they should all be different.
		 *   Those conditions being satisfied, the interpretation is that you are listing those simple roots
		 *   such that the corresponding root subgroup is in the unipotent radical.
		 */

	    
	    String parabolicSpecifier = parseParabolic(args[1],g);
	    
	    /* args[2] should specify a piece.    Can do it in one of two ways 
	     * (a) an integer for each root listed in args[1]
	     * (b) a single integer.
	     * 
	     */
	    
	    String pieceSpecifier = parsePiece(args[2], args[1].length());
	    
	    /*
	     * get list of roots in the specified piece from LiE
	     */
	    LiE lie = new LiE(  );
	    
	    String command = "-unique(-piece("+pieceSpecifier+","+parabolicSpecifier+","+args[0]+"))";
	    /*I want the roots ordered with the highest ones first and the lowest ones last.
	     * This is so that the matrix of the sunipotent attached to a positive 
	     * root comes out upper triangular. 
	     * 
	     * The natural order in LiE will put the lowest weights first.  We negate all 
	     * the roots, put the negatives in LiE order with "unique" and then negate
	     * them again. This produces the desired order.
	     * 
	     */
	    
	    System.out.println(command+"\n");
	    
	    int[][] pieceRoots = Algorithms.readLiEMatrix(lie.exec(command, OutputPref.SHOW));
	    
	    /*Compute the matrix.  
	     * 
	     * It is convenient to think of the rows and columns of this matrix as being indexed the roots of T in our piece V. 
	     * 
	     * We describe how to compute the matrix for a toral element, for a simple unipotent, and for a weyl element.  
	     * This is, of course, plenty to compute it for an arbitrary element, by the Bruhat decomposition.  
	     * 
	     * For a toral it's easy.  Let t be the toral and b any root of T in V.  Write [g]_V for the matrix of g in V, 
	     * relative to the basis of V which is a subset of our fixed Chevalley basis.  Then [t]_V is diagonal 
	     * and for each root b the (b,b) entry is b(t).
	     * 
	     * Now take a root a of T in M and an element r of the additive group.  Take roots b and c of T in V.  Then the simplest
	     * thing to say is that X_a determines an endomorphism of V with matrix [X_a]_V such that the (b,c) entry is N(a,b) if 
	     * c=a+b and 0 otherwise.  The matrix of x_a(r) is then the matrix exponential of r X_a.
	     * 
	     * Finally, for a Weyl element the answer is easy to read from Weyl signs...
	     * 
	     * The matrix we seek will have entries which are Polynomials with integral coefficients except (obviously) in the 
	     * case of a toral element.  Therefore we implement only for the other two types for now, putting off possible implementation
	     * for torals or in general until such time as rational functions are fully implemented.  
	     * 
	     */
		Matrix answer = Matrix.id(pieceRoots.length);
		for( int i = 3; i < args.length; i++)
		    answer = answer.times(matrix(args[i], pieceRoots, g));
		
		System.out.print(answer.toMathematica());

	    
	    
	} catch (ParabolicException e) {
	    // TODO Auto-generated catch block
	    System.out.println("Second argument -- "+args[1] +"-- could not be parsed as a parabolic in "+args[0]+".\n" +
	    		"required format is to list the simple roots that are in the unipotent radical,\n" +
	    		"all mashed together.  Example: 134");
	    e.printStackTrace();
	    
	} catch (ParabolicPieceException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (IOException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (PolynomialReadException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (NotARootException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (NoSuchElementException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (SunipotentFormatException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	} catch (MatrixMultiplicationException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
	
	
	
    
    }
    
    private static Matrix matrix(String string, int[][] vRoots, Group g) throws PolynomialReadException, NotARootException, NoSuchElementException, SunipotentFormatException, MatrixMultiplicationException {
	/* For now, this method should check whether the string specifies a Sunipotent, and if 
	 * not, check whether it specifies a Weyl element, 
	 * and if not, throw an exception.
	 */
	Matcher s = Algorithms.suniString(g).matcher(string);
	if(s.matches()){
	    Sunipotent suni = Sunipotent.read(string,g);
	    return Matrix.matrixOf(suni, vRoots, g);
	    
	    
	}
	
	s=Algorithms.weylWord(g).matcher(string);
	
	if( s.matches()){
	    return Matrix.matrixOfWeylWord(string, vRoots, g);
	
	}
	throw new NoSuchElementException();
    }

    /*
     * Idea here is to check the format of a string which is supposed to specify a piece, 
     * and then parse it from a convenient-to-type format into a format for LiE.
     * 
     */
    private static String parsePiece(String string, int ParabolicLength) throws ParabolicPieceException {
	if((string.length() != 1)&&(string.length() != ParabolicLength) )
	    throw new ParabolicPieceException();
	StringBuilder sb = new StringBuilder("[");
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<48||string.charAt(i)>57){
		throw new ParabolicPieceException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();

    
    }

    /*The purpose of this method is twofold:  (1) to validate user input, and (2) to allow the main program to 
     * accept input in the easiest-for-the-user format, yet have the string in the correct format for 
     * feeding to LiE.  
     * 
     */
    private static String parseParabolic(String string, Group g) throws ParabolicException {
	
	StringBuilder sb = new StringBuilder("[");
	
	for( int i =0; i < string.length(); i++)
	{
	    if(string.charAt(i)<49||string.charAt(i)>48+g.getRank()){
		throw new ParabolicException(  );
	    }
	    sb.append(string.charAt(i));
	    sb.append(i==string.length()-1?"]":",");
	}
	
	return sb.toString();
    }

}
